package com.cicoding.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.cicoding.bean.ErrorProblem;
import com.cicoding.bean.Problem;
import com.cicoding.bean.ShiroUser;
import com.cicoding.constant.GradeLevel;
import com.cicoding.dao.ErrorProblemMapper;
import com.cicoding.dao.ProblemMapper;
import com.cicoding.enums.BizCodeEnum;
import com.cicoding.excption.BizException;
import com.cicoding.request.GenerateRequest;
import com.cicoding.service.ProblemService;
import com.cicoding.utils.Userinfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Author:chandfy
 * @Description:
 * @Date:2022/12/8
 */
@Service
public class ProblemServiceImpl implements ProblemService {
    @Autowired
    private ErrorProblemMapper errorProblemMapper;
    @Autowired
    private ProblemMapper problemMapper;
    @Override
    public Set<String> generate(GenerateRequest generateRequest) {
        //TODO获取用户年纪
        int grade = 1;
        int bracketNum = 0;
        Map<String, Integer> ansMap = new HashMap<>();
        if (generateRequest.getBracketNum() != null) {
            bracketNum = generateRequest.getBracketNum();
        }
        if (generateRequest.getNum() == null || generateRequest.getNum() == 0) {
            throw new BizException(BizCodeEnum.GENERATE_PROBLEM_FAIL_BY_ZERO);
        }
        if (generateRequest.getInputGrade() != null) {
            grade = generateRequest.getInputGrade();
        }
        int num = generateRequest.getNum();
        Set<String> problemSet = this.generateProblem(grade, bracketNum, num);

        for (String s : problemSet) {
            ansMap.put(s, calculate(s));
        }
        //TODO将ansMap存数据库


         return problemSet;
    }

    private Set<String> generateProblem(int grade, int bracketNum, int num) {
        Set<String> problemSet = new HashSet<>();
        int maxOpNum = GradeLevel.OP_NUM[grade];
        boolean isContainBracket = GradeLevel.GENERATE_BRACKET[grade];
        Random random = new Random();
        while (problemSet.size() < bracketNum) {
            StringBuilder sb = new StringBuilder();
            int n = Math.max(3, random.nextInt(maxOpNum));
            for (int j = 1; j <= n; j++) {
                sb.append(random.nextInt(31));
                if (j != n)
                    sb.append(',');
                if (j != n) {
                    char op = getOneOp();
                    while (op == '(') {
                        op = getOneOp();
                    }
                    sb.append(op);
                    sb.append(',');
                }
            }
            String s = sb.toString();
            //System.out.println(s);
            String pro = addBracket(s);
            if (calculate(pro) != -1) {
                problemSet.add(pro);

            }
        }

        while (problemSet.size() < num) {
            int n = Math.max(2, random.nextInt(maxOpNum + 1));
            StringBuilder t = new StringBuilder();
            boolean flag = false;
            for (int i = 1; i <= n; i++) {
                t.append(random.nextInt(21));
                if (i != n)
                    t.append(',');
                if (i != n) {
                    char op = getOneOp();
                    if (isContainBracket && op == '(') {
                        flag = true;
                    }
                    while (op == '(') {
                        op = getOneOp();
                    }
                    t.append(op);
                    t.append(',');
                }
            }
            String rePro = t.toString();
            if (flag) {
                rePro = addBracket(rePro);
            }
            rePro = sp(rePro);
            //   System.out.println(t.toString());
            if (calculate(rePro) != -1) {
                problemSet.add(rePro);
            }
        }
        return problemSet;
    }

    private char getOneOp() {
        Random random = new Random();
        String str = "+-÷x(";
        char[] c = str.toCharArray();
        return c[random.nextInt(5)];
    }

    private String addBracket(String s) {
        Random random = new Random();
        String[] ari = s.split(",");
        int pos = random.nextInt(ari.length - 2);
        pos = pos % 2 == 0 ? pos : pos - 1;
        int posEnd = random.nextInt(ari.length);
        posEnd = posEnd % 2 == 0 ? posEnd : posEnd - 1;
        posEnd = Math.max(pos + 2, posEnd);
        StringBuilder temp = new StringBuilder();
        for (int k = 0; k < ari.length; k++) {
            if (pos == k) {
                temp.append('(');
                temp.append(ari[k]);
            } else if (posEnd == k) {
                temp.append(ari[k]);
                temp.append(')');
            } else {
                temp.append(ari[k]);
            }

        }
        return temp.toString();
    }

    private String sp(String s) {
        String[] ari = s.split(",");
        StringBuilder sb = new StringBuilder();
        for (int k = 0; k < ari.length; k++) {
            sb.append(ari[k]);
        }
        return sb.toString();
    }

    private boolean eval(Stack<Integer> num, Stack<Character> op) {
        if (num.empty() || op.empty()) return false;
        int b = num.pop();
        if (num.empty() || op.empty()) return false;
        int a = num.pop();
        char c = op.pop();

        if (c == '+') {
            if (a + b > 1000) {
                return false;
            }
            a += b;
        } else if (c == '-') {
            if (a < b) {
                return false;
            }
            a -= b;
        } else if (c == 'x') {
            if (a * b > 1000) {
                return false;
            }
            a *= b;
        } else {
            if (b != 0 && a % b == 0) {
                a /= b;
            } else {
                return false;
            }
        }
        num.push(a);
        return true;
    }
    @Override
    public int calculate(String s) {
        Map<Character, Integer> map = new HashMap<>();
        map.put('+', 1);
        map.put('-', 1);
        map.put('x', 2);
        map.put('÷', 2);
        Stack<Character> op = new Stack<>();//存运算符号
        Stack<Integer> num = new Stack<>();//存数字
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            //判断c字符是不是数字
            if (Character.isDigit(c)) {
                int x = 0, j = i;
                //数字可能会是多位数，
                while (j < s.length() && Character.isDigit(s.charAt(j))) {
                    x = x * 10 + s.charAt(j) - '0';
                    j++;
                }
                num.push(x);//将数字x存入数字栈栈顶
                i = j - 1;//重新赋值i
            } else if (c == '(') {
                op.push(c); // 将左括号存入字符栈栈顶
            } else if (c == ')') {
                //如果栈顶不等于左括号，一直计算下去；
                while (op.peek() != '(') {
                    if (!eval(num, op)) return -1;
                }
                op.pop(); // 将左括号弹出栈顶
            } else { //如果是正常字符
                while (!op.empty() && op.peek() != '(' && map.get(op.peek()) >= map.get(c)) {
                    if (!eval(num, op)) {
                        return -1;
                    }
                }
                op.push(c);
            }
        }
        while (!op.empty()) if (!eval(num, op)) return -1;
        return num.peek();
    }

    @Override
    public List<ErrorProblem> errorProblemList(int id) {
        EntityWrapper<ErrorProblem> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id",id);
        wrapper.orderBy("counts",false);
        List<ErrorProblem> errorProblemList = errorProblemMapper.selectList(wrapper);
        return errorProblemList;
    }

    @Override
    public int problemNum() {
        List<Problem> problemList = problemMapper.selectList(null);
        return problemList.size();
    }
}
